﻿using Devil;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Jobs;

namespace GameToolkit
{
    public static class NativeArrayUtil
    {

        public static double GetNativeSize(ulong bytes, ENativeSize unit)
        {
            return bytes / (double)(1LU << (int)unit);
        }

        public static void Clear<T>(this NativeArray<T> arr, ref int count) where T : struct
        {
            count = 0;
        }

        public static void Clear<T>(this T[] arr, ref int count)
        {
            for (int i = 0; i < count; i++)
            {
                arr[i] = default;
            }
            count = 0;
        }

        public static bool Add<T>(this NativeArray<T> arr, ref int count, T value) where T : struct
        {
            if (count < arr.Length)
            {
                arr[count++] = value;
                return true;
            }
            else
            {
                return false;
            }
        }

        public static bool Add<T>(this T[] arr, ref int count, T value)
        {
            if (count < arr.Length)
            {
                arr[count++] = value;
                return true;
            }
            else
            {
                return false;
            }
        }

        public static bool Insert<T>(this NativeArray<T> arr, ref int count, int index, T value) where T : struct
        {
            if (count < arr.Length)
            {
                for (int i = count; i > index; i--)
                {
                    arr[i] = arr[i - 1];
                }
                arr[index] = value;
                count++;
                return true;
            }
            return false;
        }

        public static bool Insert<T>(this T[] arr, ref int count, int index, T value)
        {
            if (count < arr.Length)
            {
                for (int i = count; i > index; i--)
                {
                    arr[i] = arr[i - 1];
                }
                arr[index] = value;
                count++;
                return true;
            }
            return false;
        }

        // 删除 index 位置数据, 并由末位数据填充
        public static void RemoveAtSwapBack<T>(this NativeArray<T> arr, ref int count, int index) where T : struct
        {
            if (index >= count || index < 0)
                throw new System.IndexOutOfRangeException();
            count--;
            if (index < count)
            {
                arr[index] = arr[count];
            }
        }

        public static void RemoveAtSwapBack<T>(this T[] arr, ref int count, int index)
        {
            if (index >= count || index < 0)
                throw new System.IndexOutOfRangeException();
            count--;
            if (index < count)
            {
                arr[index] = arr[count];
            }
            arr[count] = default;
        }

        public static void RemoveAt<T>(this NativeArray<T> arr, ref int count, int index) where T : struct
        {
            if (index >= count || index < 0)
                throw new System.IndexOutOfRangeException();
            count--;
            for (int i = index; i < count; i++)
            {
                arr[i] = arr[i + 1];
            }
        }

        public static void RemoveAt<T>(this T[] arr, ref int count, int index)
        {
            if (index >= count || index < 0)
                throw new System.IndexOutOfRangeException();
            count--;
            for (int i = index; i < count; i++)
            {
                arr[i] = arr[i + 1];
            }
            arr[count] = default;
        }

        public static void EnsureNativeSize<T>(ref NativeArray<T> arr, int dataCount, int newsize, Allocator allocator = Allocator.Persistent, float resizeRatio = 1.5f) where T : struct
        {
            if (!arr.IsCreated || arr.Length < newsize)
            {
                var size = Mathf.Max(Mathf.CeilToInt(arr.Length * resizeRatio), newsize);
                var cp = new NativeArray<T>(size, allocator);
                if (dataCount > 0)
                {
                    NativeArray<T>.Copy(arr, cp, dataCount);
                }
                if (arr.IsCreated)
                {
                    arr.Dispose();
                }
                arr = cp;
            }
        }

        public static void EnsureNativeSize<T>(ref T[] arr, int dataCount, int newSize, float resizeRatio = 1.5f)
        {
            if (newSize > 0 && (arr == null || arr.Length < newSize))
            {
                var size = Mathf.Max(arr == null ? (newSize + 4) : Mathf.CeilToInt(arr.Length * resizeRatio), newSize);
                var cp = new T[size];
                if (dataCount > 0)
                    System.Array.Copy(arr, cp, dataCount);
                arr = cp;
            }
        }

        public static void EnsureNativeSize(ref TransformAccessArray arr, int newsize, float resizeRatio = 1.5f)
        {
            if (!arr.isCreated)
            {
                arr = new TransformAccessArray(newsize);
            }
            else if (arr.capacity < newsize)
            {
                var size = Mathf.Max(Mathf.CeilToInt(arr.capacity * resizeRatio), newsize);
                arr.capacity = size;
            }
        }

        public static int IndexOf<T>(this IList<T> lst, FilterDelegate<T> pattern)
        {
            var len = lst == null ? 0 : lst.Count;
            for (int i = 0; i < len; i++)
            {
                if (pattern(lst[i]))
                    return i;
            }
            return -1;
        }

    }

    public struct SliceList<T> : IList<T>, System.IEquatable<SliceList<T>>
    {
        IList<T> mLst;
        int mStart;
        int mCount;
        public T this[int index] { get { return mLst[index + mStart]; } set { throw new System.Exception("Readonly List"); } }

        public int Count { get { return mCount; } }

        public int NextSliceIndex { get { return mStart + mCount; } }

        public bool IsReadOnly { get { return true; } }

        public SliceList([NotNull]IList<T> lst, int start, int count)
        {
            mLst = lst;
            mStart = start;
            mCount = count;
        }

        public SliceList([NotNull]IList<T> lst)
        {
            mLst = lst;
            mStart = 0;
            mCount = lst == null ? 0 : lst.Count;
        }

        public bool Equals(SliceList<T> other)
        {
            return mLst == other.mLst && mStart == other.mStart && mCount == other.mCount;
        }

        public void Add(T item)
        {
            throw new System.Exception("Readonly List");
        }

        public void Clear()
        {
            throw new System.Exception("Readonly List");
        }

        public bool Contains(T item)
        {
            return IndexOf(item) != -1;
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            for (int i = 0; i < mCount; i++)
            {
                array[arrayIndex + i] = mLst[mStart + i];
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new SliceEnumerator<T>(mLst, mStart, mCount);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new SliceEnumerator<T>(mLst, mStart, mCount);
        }

        public int IndexOf(T item)
        {
            var len = Mathf.Min(mStart + mCount, mLst.Count);
            for (int i = mStart; i < len; i++)
            {
                var it = mLst[i];
                if (it is System.IEquatable<T> && ((System.IEquatable<T>)it).Equals(item))
                    return i - mStart;
                if (it != null && it.Equals(item))
                    return i - mStart;
            }
            return -1;
        }

        public void Insert(int index, T item)
        {
            throw new System.Exception("Readonly List");
        }

        public bool Remove(T item)
        {
            throw new System.Exception("Readonly List");
        }

        public void RemoveAt(int index)
        {
            throw new System.Exception("Readonly List");
        }

    }

    public struct SliceEnumerator<T> : IEnumerator<T>
    {
        IList<T> values;
        //T[] values;
        int start;
        int end;
        int index;

        public SliceEnumerator([NotNull]IList<T> values, int start, int length)
        {
            this.values = values;
            this.start = start;
            this.end = start + length;
            index = start - 1;
        }

        public T Current { get { return values[index]; } }

        object IEnumerator.Current { get { return values[index]; } }

        public void Dispose()
        {
            values = null;
            start = 0;
            end = 0;
            index = 0;
        }

        public bool MoveNext()
        {
            return ++index < end;
        }

        public void Reset()
        {
            index = start - 1;
        }
    }

    public struct WrapEnumerator<TInput, TOutput> : IEnumerator<TOutput>
    {
        IEnumerator<TInput> src;
        GetterDelegate<TInput, TOutput> getter;
        public WrapEnumerator([NotNull]IEnumerator<TInput> src, [NotNull]GetterDelegate<TInput, TOutput> getter)
        {
            this.src = src;
            this.getter = getter;
        }

        object IEnumerator.Current { get { return getter(src.Current); } }

        TOutput IEnumerator<TOutput>.Current { get { return getter(src.Current); } }

        public void Dispose()
        {
            src.Dispose();
        }

        public bool MoveNext()
        {
            return src.MoveNext();
        }

        public void Reset()
        {
            src.Reset();
        }
    }
}